package com.tracelog.core.dao;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.tracelog.core.config.TraceLogProperties;
import com.tracelog.core.enums.LogLevel;
import com.tracelog.core.model.LogRecord;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.Date;
import java.util.concurrent.CompletableFuture;

/**
 * 默认日志存储实现
 *
 * @author tracelog
 * @since 1.0.0
 */
@Slf4j
@RequiredArgsConstructor
public class DefaultLogRecordDao implements LogRecordDao {

    private final TraceLogProperties properties;

    @Override
    public void save(LogRecord logRecord) {
        if (properties.isAsync()) {
            saveAsync(logRecord);
        } else {
            doSave(logRecord);
        }
    }

    /**
     * 异步保存日志
     *
     * @param logRecord 日志记录
     */
    @Async
    public CompletableFuture<Void> saveAsync(LogRecord logRecord) {
        return CompletableFuture.runAsync(() -> doSave(logRecord));
    }

    /**
     * 执行保存逻辑
     *
     * @param logRecord 日志记录
     */
    private void doSave(LogRecord logRecord) {
        try {
            // 格式化日志记录
            String formattedLog = formatLogRecord(logRecord);
            
            // 打印到控制台
            if (properties.isPrintToConsole()) {
                printToConsole(logRecord, formattedLog);
            }
            
            // 保存到文件
            if (properties.isFileOutput()) {
                saveToFile(formattedLog);
            }
        } catch (Exception e) {
            log.error("Failed to save log record", e);
        }
    }

    /**
     * 格式化日志记录
     *
     * @param logRecord 日志记录
     * @return 格式化后的日志
     */
    private String formatLogRecord(LogRecord logRecord) {
        StringBuilder sb = new StringBuilder();
        
        // 添加时间戳
        sb.append("[").append(DateUtil.formatDateTime(Date.from(logRecord.getStartTime()))).append("] ");
        
        // 添加级别
        sb.append("[").append(logRecord.getLevel()).append("] ");
        
        // 添加模块
        if (logRecord.getModule() != null) {
            sb.append("[").append(logRecord.getModule()).append("] ");
        }
        
        // 添加描述
        if (logRecord.getDescription() != null) {
            sb.append(logRecord.getDescription()).append(" ");
        }
        
        // 添加请求信息
        sb.append("- ").append(logRecord.getMethod()).append(" ").append(logRecord.getUri()).append(" ");
        
        // 添加耗时
        Duration duration = logRecord.getExecutionTime();
        if (duration != null) {
            long millis = duration.toMillis();
            if (millis > 1000) {
                sb.append("(").append(millis / 1000.0).append("s) ");
            } else {
                sb.append("(").append(millis).append("ms) ");
            }
        }
        
        // 添加客户端信息
        if (logRecord.getClientIp() != null) {
            sb.append("from ").append(logRecord.getClientIp()).append(" ");
        }
        
        // 添加异常信息
        if (logRecord.getExceptionMessage() != null) {
            sb.append("ERROR: ").append(logRecord.getExceptionMessage()).append(" ");
        }
        
        // 添加详细JSON信息
        sb.append("\nDETAILS: ").append(JSONUtil.toJsonStr(logRecord));
        
        return sb.toString();
    }

    /**
     * 打印到控制台
     *
     * @param logRecord    日志记录
     * @param formattedLog 格式化后的日志
     */
    private void printToConsole(LogRecord logRecord, String formattedLog) {
        LogLevel level = logRecord.getLevel();
        
        switch (level) {
            case TRACE:
                log.trace(formattedLog);
                break;
            case DEBUG:
                log.debug(formattedLog);
                break;
            case INFO:
                log.info(formattedLog);
                break;
            case WARN:
                log.warn(formattedLog);
                break;
            case ERROR:
                log.error(formattedLog);
                break;
            default:
                log.info(formattedLog);
        }
    }

    /**
     * 保存到文件
     *
     * @param formattedLog 格式化后的日志
     */
    private void saveToFile(String formattedLog) {
        try {
            String logFilePath = properties.getLogFilePath();
            Path path = Paths.get(logFilePath);
            
            // 确保目录存在
            Files.createDirectories(path.getParent());
            
            // 如果文件不存在，创建文件
            if (!Files.exists(path)) {
                Files.createFile(path);
            }
            
            // 写入日志记录
            Files.write(
                path,
                (formattedLog + System.lineSeparator()).getBytes(),
                StandardOpenOption.APPEND
            );
        } catch (IOException e) {
            log.error("Failed to write log to file", e);
        }
    }
} 